﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Collections;
using System.ComponentModel;

namespace Luna.Framework.Manifest {
	public class PropertyManifestProcessor : IManifestProcessor {
		public bool isSupport(IConfigurationManifest manifest, Type hostManifestType) {
			return "property".Equals(manifest.Name);
		}

		public static object getValue(object host, string rawValue, IConfigurationManifest manifest, IAppContext context, Type valueType) {
			object value = null;
			if (rawValue.StartsWith("@@"))
				value = rawValue.Substring(1);
			else if (rawValue.Equals("@manifest"))
				value = manifest;
			else if (rawValue.Equals("@this"))
				value = host;
			else if (rawValue.Equals("@context"))
				value = context;
			else if (rawValue.Equals("@context.host"))
				value = context.Host;
			else if (rawValue.StartsWith("@")) {
				Uri uri = new Uri(rawValue.Substring(1));
				value = Framework.resolveObject(context, uri, manifest);
				if (value != null)
					value = Framework.Manifest.applyManifest<object>(manifest, context, value);
			}
			else
				value = rawValue.convertTo(valueType);
			return value;
		}

		public static void setPropertyValue(object host, string propertyName, string propertyValue) {
			PropertyInfo prop = host.getMember<PropertyInfo>(propertyName, false);
			if (prop != null)
				prop.SetValue(host, propertyValue.convertTo(prop.PropertyType), null);
		}

		public virtual bool process(IManifestManager manager, IConfigurationManifest manifest, object host, IAppContext context) {
			PropertyInfo prop = host.getMember<PropertyInfo>(manifest.getProperty<string>("name", ""), false);
			if (prop != null) {
				string rawValue = manifest.getProperty<string>("value").expandString();
				object value = getValue(host, rawValue, manifest, context, prop.PropertyType);
				prop.SetValue(host, value, null);
				return true;
			}
			return false;
		}	
	}

	class ListPropertyManifestProcessor : IManifestProcessor {
		public bool isSupport(IConfigurationManifest manifest, Type hostManifestType) {
			return "list-property".Equals(manifest.Name);
		}

		public virtual bool process(IManifestManager manager, IConfigurationManifest manifest, object host, IAppContext context) {
			PropertyInfo prop = host.getMember<PropertyInfo>(manifest.getProperty<string>("name", ""), false);
			if (prop != null) {
				object list = prop.GetValue(host, null);
				Type[] typeParam = prop.PropertyType.GetGenericArguments();
				Type elementType = manifest.getProperty<string>("elementType")
							.getType().getDefaultValue<Type>(typeParam.Length > 0 ? typeParam[0] : typeof(string));
				if (manifest.hasChild() && (list is IList)) 
					foreach (IConfigurationManifest subchild in manifest.getChildren())
						if (subchild.Name.Equals("add")) {
							object value = PropertyManifestProcessor.getValue(host, subchild.getProperty<string>("value").expandString(), subchild, context, elementType);
							(list as IList).Add(value);
						}
				return true;
			}
			return false;
		}	
	}

	class DictionaryPropertyManifestProcessor : IManifestProcessor {

		public bool isSupport(IConfigurationManifest manifest, Type hostManifestType) {
			return "dictionary-property".Equals(manifest.Name);
		}

		public bool process(IManifestManager manager, IConfigurationManifest manifest, object host, IAppContext context) {
			PropertyInfo prop = host.getMember<PropertyInfo>(manifest.getProperty<string>("name", ""), false);
			if (prop != null) {
				object list = prop.GetValue(host, null);

				// try to detect the type of Key and Value of the dictionary. If not success, 
				// we assume the Key, Value are strings.
				Type[] typeParam = prop.PropertyType.GetGenericArguments();
				Type elementType = manifest.getProperty<string>("elementType")
					.getType().getDefaultValue<Type>(typeParam.Length > 0 ? typeParam[0] : typeof(string));
				Type keyType = manifest.getProperty<string>("keyType")
					.getType().getDefaultValue<Type>(typeParam.Length > 1 ? typeParam[1] : typeof(string));

				if (manifest.hasChild() && list is IDictionary)
					foreach (IConfigurationManifest subchild in manifest.getChildren())
						if (subchild.Name.Equals("add")) {
							object value = PropertyManifestProcessor.getValue(host, subchild.getProperty<string>("value").expandString(), subchild, context, elementType);
							object key = PropertyManifestProcessor.getValue(host, subchild.getProperty<string>("name").expandString(), subchild, context, keyType);
							(list as IDictionary).Add(key, value);
						}
				return true;
			}
			return false;
		}
	}
}


